package gbench.appdemo.myworld.market.commodity;

import gbench.common.tree.LittleTree;
import gbench.common.tree.LittleTree.IRecord;
import gbench.common.tree.LittleTree.Node;
import gbench.common.tree.LittleTree.Tuple2;

import static gbench.common.tree.LittleTree.IRecord.*;
import static gbench.common.tree.LittleTree.Tuple2.*;

import java.text.MessageFormat;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 商品对象：商品是对产品的包装。
 * 一个商品是(Product,Quantity)的二元组。
 * @author gbench
 *
 */
public class Commodity extends Valuable implements Comparable<Commodity> {
    
    /**
     * 产品对象
     * @author gbench
     *
     */
    public class Product implements IProduct{
        
        public Product(final String name,final IRecord bom) {
            this.name = name;
            this.bom = bom;
            this.attributes = REC();
        }

        @Override
        public String getName() {
            return name;
        }

        @Override
        public IRecord getComponents() {
            return bom;
        }
        
        @Override
        public IRecord getAttributes() {
            return attributes;
        }
        
        final String name;
        final IRecord bom;
        final IRecord attributes;
    }// Product

    /**
     * 
     * @param name 商品名称
     * @param qty 数量
     */
    public Commodity(String name, Number qty,String unitName,IRecord bom) {
        super(name,unitName);
        product = new Product(name,bom);
        this.quality = qty.doubleValue();
    }

    public IProduct getProduct() {
        return product;
    }
    
    public void setProduct(IProduct product) {
        this.product = product;
    }
    
    public Double getQuality() {
        return quality;
    }
    
    public void setQuality(Double quality) {
        this.quality = quality;
    }
    
    /**
     * 非递归式成分遍历:
     * @param bics 子元素的 读取
     */
    @SuppressWarnings("unchecked")
    public void traverse(BiConsumer<String,Tuple2<Commodity,Number>> bics) {
        this.getComponents().dfs_forall((path,elems)->{
            elems.stream().filter(e->e instanceof Tuple2)
            .map(e->{
                final var tup = (Tuple2<Object,Object>)e;
                if(! (tup._1() instanceof Commodity)) return null;
                else if(! (tup._2() instanceof Number)) return null;
                else return (Tuple2<Commodity,Number>)e;
            }).forEach(e->bics.accept(path, e));
        });
    }
    
    /**
     * 遍历 出所有的 raw成分节点
     * @param bics (Commodity,Number)->{} 二元消费函数
     * @param n 倍数：成分数量
     * @param prefix 路径前缀
     * @return 理算几点集合
     */
    @SuppressWarnings("unchecked")
    public void traverse2(BiConsumer<String,Tuple2<Commodity,Number>> bics,Number n,String prefix) {
        // 遍历所有结构成分
        this.getComponents().dfs_forall( (path, elems) -> {
            elems.stream().filter(e->e instanceof Tuple2) // 过滤掉非 Tuple2的元素类型
            .map(e->{
                final var tup = (Tuple2<Object,Object>)e;
                if(! (tup._1() instanceof Commodity)) return null;
                else if(! (tup._2() instanceof Number)) return null;
                else {
                    final var tuple2 = (Tuple2<Commodity,Number>)e;
                    final var commodity = tuple2._1();
                    if(!commodity.isRaw()){// 非运料产品
                        commodity.traverse2(bics,n.doubleValue()*tuple2._2().doubleValue(),path);
                        return null;// 非原料产品不予处理
                    } else {
                        return TUP2(tuple2._1(),(Number)(tuple2._2().doubleValue()*n.doubleValue()));
                    }//if
                }//if
            }).filter(e->e!=null).forEach(e->{
                bics.accept(prefix+path, e);
            }); // forEach
        });// dfs_forall
    }
    
    /**
     * 成分节点遍历(包含自身）
     * @param (parents:父节点李彪,node 当前节点)->{} 
     */
    public void forEach(final BiConsumer<List<Tuple2<Commodity,Number>>,Tuple2<Commodity,Number>> bics) {
        this.forEach(bics,new LinkedList<>());
    }
    
    /**
     * 成分节点节点遍历(包含自身）
     * @param bics (parents:父节点李彪,node 当前节点)->{}
     * @param parents 腹肌诶按列表
     */
    protected void forEach(final BiConsumer<List<Tuple2<Commodity,Number>>,Tuple2<Commodity,Number>> bics, 
        final List<Tuple2<Commodity,Number>> parents) {
        
        bics.accept(parents, TUP2(this,this.quality));// 处理本身节点
        this.traverse((path,tup)->{// 遍历子节点
            final var item = tup._1();
            final var pp = new LinkedList<Tuple2<Commodity,Number>>(parents==null?new LinkedList<>():parents);
            pp.add(TUP2(this,this.quality));
            if(item.isRaw()) {// 遍历但节点
                bics.accept(pp, tup);;
            }else {
                item.forEach(bics,pp);
            }
        });
    }
    
    /**
     * 获取原谅清单
     * @param n Bom 的结构的数量
     * @return
     */
    public List<IRecord> getBomOfRaw(final Number n){
        final var ll = new LinkedList<IRecord>();
        
        this.traverse2((path,e)->{
            ll.add(REC("path",path,"name",e._1(),"quantity",e._2()));
        },n,"");
        
        return ll;
    }

    /**
     * 获取原料清单
     * @return
     */
    public Stream<IRecord> getBom(){
        return this.getBomOfRaw().stream();
    }

    /**
     * 获取原料清单
     * @return
     */
    public List<IRecord> getBomOfRaw(){
        return this.getBomOfRaw(Commodity.this.quality);
    }
    
    /**
     * 原料清单表
     * @return
     */
    public Map<Commodity,Number> getBomTable(){
        Map<Commodity,Number> map = new HashMap<>();
        this.getBomOfRaw().stream().collect(Collectors.groupingBy(e->e.get("name"))).forEach((k,vv)->{
            final var amount = vv.stream().map(e->e.i4("quantity")).collect(Collectors.summarizingDouble(e->e)).getSum();
            map.put((Commodity)k,(Number)amount);
        });
        return map;
    }
    
    /**
     * 直接返回产品的 配料清单：结构成分
     * @return
     */
    public IRecord getComponents() {
        return this.getProduct().getComponents();
    }
    
   
    /**
     * 添加一个 fld 的成分结构 
     * @param fld 成分字段名称
     * @param component 成分对象 实例
     * @param qty component 的数量
     * @return 本身对象 方便实现链式编程
     */
    public Commodity addComponent(final String fld , final Commodity component, final Number qty) {
        if(! (fld==null||component==null||qty==null) ) { // 当且建档参数非空的时候才给与设置 结构成分
            this.getProduct().getComponents().add(fld,TUP2(component,qty));
        }//if
        return this;
    }
   
    /**
     * 是否是原料商品:原料商品是指 不包含结构成分的产品。
     * @return 是否是原料商品
     */
    public boolean isRaw() {
        return this.product.isRaw();
    }
    
    public static Commodity COMMODITY(String name, Number qty,String unitName,IRecord bom) {
        return new Commodity(name, qty,unitName,bom);
    }
    
    public static Commodity COMMODITY(String name, Number qty,String unitName) {
        return COMMODITY(name, qty,unitName,REC());
    }
    
    public static Commodity COMMODITY(String name, Number qty) {
        return COMMODITY(name, qty,"个",REC());
    }
    
    public static Commodity COMMODITY(String name,String unitName) {
        return COMMODITY(name, 1,unitName,REC());
    }
    
    public static Commodity COMMODITY(String name,String unitName,IRecord bom) {
        return COMMODITY(name, 1,unitName,bom);
    }

    public static Commodity COMMODITY(String name) {
        return COMMODITY(name, 1);
    }
    
    /**
     * 生成商品目录
     * @param root Node根节点, Node<Record> 的getName 奖杯用作 成分名称。
     * @return 商品目录
     */
    public static Map<String, Commodity> catalog(Node<IRecord> root){
        final Map<String, Commodity> commodityCatalog = new LinkedHashMap<String, Commodity>();// 商品目录
        LittleTree.traverse2(root, (node, level) -> {// 倒序遍历
            final var commodity = commodityCatalog.computeIfAbsent(node.getName(), Commodity::COMMODITY);// 生成商品项目
            node.getChildren().forEach(child -> {// 把子节点作为 commodityItem 成分添加入 对象结构。
                commodity.addComponent(child.getName(), commodityCatalog.get(child.getName()), 1);
            });// forEach
        });// traverse2
        return commodityCatalog;
    }

    public static Commodity CMDT(String name) {
        return COMMODITY(name, 1);
    }
    
    @Override
    public String toString() {
        return MessageFormat.format("(name:{0},unit:{1},qty:{2})", this.getName(),this.getUnitName(),this.getQuality());
    }
    
    @Override
    public int compareTo(Commodity o) {
        if(o==null)return 1;
        return this.toString().compareTo(o.toString());
    }
    
    /**
     * 
     * @return
     */
    public IRecord getAttributes() {
        return attributes;
    }

    /**
     * 
     * @param attributes
     */
    public void setAttributes(IRecord attributes) {
        this.attributes = attributes;
    }
    
    /**
     * 设置属性
     * @param name
     * @param value
     * @return 自身方便实现链式编程
     */
    public Commodity setAttribute(String name,Object value) {
        this.attributes.set(name, value);
        return this;
    }
    
    /**
     * 获取属性
     * @param name
     * @return
     */
    public Object getAttribute(String name) {
        return this.attributes.get(name);
    }
    
    /**
     * getAttribute 的名称简写<br>
     * 获取属性
     * @param name
     * @return
     */
    public Object attr(String name) {
        return this.getAttribute(name);
    }

    private IProduct product; // product 数量
    private Double quality;//  product 的单位数量
    private IRecord attributes;
    
    
}